package tool.prototypes;

import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.ModifierSet;
import japa.parser.ast.body.Parameter;
import japa.parser.ast.expr.AssignExpr;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.type.ClassOrInterfaceType;
import japa.parser.ast.type.PrimitiveType;
import japa.parser.ast.type.ReferenceType;
import japa.parser.ast.type.Type;
import japa.parser.ast.type.VoidType;
import japa.parser.ast.type.WildcardType;
import japa.parser.ast.visitor.VoidVisitorAdapter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

	/**
	 * @author jwb09119
	 * @date 2013/11/19
	 * @prototype
	 * 
	 * This visitor is intended to visit all Variables in a target java source and extract the type
	 * of the variable.  If possible I also want to extract the type of the initialising value too.
	 */

public class VariableDeclarationVisitorPrototype<A> extends VoidVisitorAdapter<A>{
	
	private List<FieldRecord> fields;
	private List<ParameterRecord> params;
	
	public VariableDeclarationVisitorPrototype () {
		
		fields = new ArrayList<>();
		params = new ArrayList<>();
		
	}
    
    
    protected void doVisit() {
    	
    	final char SLASH = File.separatorChar;
    	
    	// creates an input stream for the file to be parsed
        FileInputStream in = null;
        
		try {
			
			in = new FileInputStream("src"+SLASH+"tool"+SLASH+"prototypes"+SLASH+"testSrc"+SLASH+"Test.java");
			
		} catch (FileNotFoundException e) {

			e.printStackTrace();
		}

        CompilationUnit cu = null;
        try {
            // parse the file
            try {
				cu = JavaParser.parse(in);
			} catch (ParseException e) {

				e.printStackTrace();
			}
        } finally {
            try {
				in.close();
			} catch (IOException e) {

				e.printStackTrace();
			}
        }

        visit(cu, null);

    	
    }
    
    
    @Override
    public void visit (Parameter n, A arg) {
    	System.out.println("\nParameter: \n" + n.toString());
    	
    	ParameterRecord pr = new ParameterRecord();
    	pr.name = n.getId().getName();
    	pr.type = getTypeName(n.getType());
    	pr.isArray = (n.getId().getArrayCount() > 0);
    	params.add(pr);
    	
    }
    
    @Override
    public void visit (FieldDeclaration n, A arg) {
    	System.out.println("\nField: \n" + n.toString());
    	
    	FieldRecord fr = new FieldRecord();
    	fr.name = n.getVariables().get(0).getId().getName();
    	fr.type = getTypeName(n.getType());
    	fr.isArray = (n.getVariables().get(0).getId().getArrayCount() > 0);
    	fr.modifiers = n.getModifiers();
    	fields.add(fr);

    	
    }
    
    @Override
    public void visit(VariableDeclarationExpr n, A arg) {
    	System.out.println("\nVariable Declaration: \n" + n.toString());
    	
    	
    }
    
    @Override
    public void visit(AssignExpr n, A arg) {
    	System.out.println("\nAssignment Expression: \n" + n.toString());
    	
    	
    }

    
    private String getTypeName (Type type) {
    	
    	String reply = "TYPE NOT RESOLVED";
    	
    	if (type instanceof ClassOrInterfaceType) {
    		
    		reply =  ((ClassOrInterfaceType) type).getName();
    		
    	} else if (type instanceof PrimitiveType) {
    		
    		reply = ((PrimitiveType) type).getType().toString();
    		
    	} else if (type instanceof ReferenceType) {
    		
    		reply = getTypeName(((ReferenceType) type).getType());
    		
    	} else if (type instanceof VoidType) {
    		
    		reply = "VOID";
    		
    	} else if (type instanceof WildcardType) {
    		
    		reply = ((WildcardType) type).toString();
    		
    	}
    	
    	return reply;
    }    
    
    
    class FieldRecord {
    	
    	private String name;
    	private String type;
    	private boolean isArray;
    	private int modifiers;
    	
    	protected FieldRecord () {
    		
    	}
    	
    	public boolean isStatic () {
    		return ModifierSet.isStatic(modifiers);
    	}
    	
    	public boolean isPrivate () {
    		return ModifierSet.isPrivate(modifiers);
    	}
    	
    	public boolean isFinal () {
    		return ModifierSet.isFinal(modifiers);
    	}

    	@Override
    	public String toString () {
    		return "\nField:\nName: "+name+"\nType: "+type+"\nIs Array: "+isArray;	
    	}
    	
    }    
    
    
    class ParameterRecord {
    	
    	private String name;
    	private String type;
    	private boolean isArray;
    	
    	protected ParameterRecord () {
    			
    	}
    	
    	@Override
    	public String toString () {
    		return "\nParameter\nName: "+name+"\nType: "+type+"\nIs Array: "+isArray;	
    	}
    	
    }


	public void printContents() {
		
		System.out.println("Contents:");
		
		for (FieldRecord f : fields) {
			System.out.println(f);
		}
		
		for (ParameterRecord p : params) {
			System.out.println(p);
		}
		
	}
    
}
